package com.samebuilding.dao.impl;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.sql.SQLException;
import java.util.List;

import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.Query;
import org.hibernate.Session;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

import com.samebuilding.dao.GenericDAO;

public class GenericDAOImpl<T, ID extends Serializable> extends
		HibernateDaoSupport implements GenericDAO<T, ID> {

	private Class<T> persistentClass;

	@SuppressWarnings("unchecked")
	public GenericDAOImpl() {
		this.persistentClass = (Class<T>) ((ParameterizedType) getClass()
				.getGenericSuperclass()).getActualTypeArguments()[0];
	}

	public Class<T> getPersistentClass() {
		return persistentClass;
	}

	@SuppressWarnings("unchecked")
	public T findById(ID id, boolean lock) {
		T entity;
		if (lock)
			entity = (T) this.getHibernateTemplate().load(getPersistentClass(),
					id, LockMode.UPGRADE);
		else
			entity = (T) this.getHibernateTemplate().load(getPersistentClass(),
					id);

		return entity;
	}

	@SuppressWarnings("unchecked")
	public List<T> findAll(final int... args) {
		if (args.length > 0) {
			List<T> list = getHibernateTemplate().executeFind(
					new HibernateCallback() {
						public Object doInHibernate(Session session)
								throws HibernateException, SQLException {
							Query query = session.createQuery("from "
									+ persistentClass.getName());
							query.setFirstResult(args[0]);
							query.setMaxResults(args[1]);
							List<T> list = query.list();
							return list;
						}
					});
			return list;
		} else {
			return getHibernateTemplate().find(
					"from " + persistentClass.getName());
		}
	}

	@SuppressWarnings("unchecked")
	public T save(T entity) {
		getSession().save(entity);
		return entity;
	}

	public void delete(T entity) {
		getSession().delete(entity);
	}

	public void delete(ID id) {
		T entity = findById(id, true);
		delete(entity);

	}

	public T saveOrUpdate(T entity) {
		getHibernateTemplate().saveOrUpdate(entity);
		return entity;
	}

	@SuppressWarnings("unchecked")
	public List<T> findByNamedQuery(final String namedQuery,
			final Object[] queryArgs, final int... paging) {
		List<T> list = getHibernateTemplate().executeFind(
				new HibernateCallback() {
					public Object doInHibernate(Session session)
							throws HibernateException, SQLException {
						Query query = session.getNamedQuery(namedQuery);
						for (int i = 0; i < queryArgs.length; i++) {
							Object arg = queryArgs[i];
							query.setParameter(i, arg);
						}

						if (paging.length > 0) {
							query.setFirstResult(paging[0]);
							query.setMaxResults(paging[1]);
						}
						List<T> list = query.list();
						return list;
					}
				});
		return list;
	}

	public long getTotalCount() {
		List list = this.getHibernateTemplate().find(
				"select count(*) from " + persistentClass.getName());
		return (Long) list.get(0);
	}

	public void flushSession() {
		getSession().flush();
	}

	public List<T> findByExmaple(T example) {
		List<T> list = this.getHibernateTemplate().findByExample(example);
		return list;
	}

}
